Remove geoip2 library
This commit is contained in:
@@ -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.'
|
|
@@ -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)
|
|
@@ -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()
|
|
@@ -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."""
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -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
|
|
||||||
<http://en.wikipedia.org/wiki/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
|
|
||||||
<http://en.wikipedia.org/wiki/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
|
|
||||||
<https://developers.google.com/adwords/api/docs/appendix/cities-DMAregions>`_.
|
|
||||||
|
|
||||||
: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 <http://www.iana.org/time-zones>`_, 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
|
|
||||||
<http://en.wikipedia.org/wiki/ISO_3166-2>`_.
|
|
||||||
|
|
||||||
: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 <http://en.wikipedia.org/wiki/Autonomous_system_(Internet)>`_
|
|
||||||
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 <http://en.wikipedia.org/wiki/Autonomous_system_(Internet)>`_ 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
|
|
||||||
<https://www.maxmind.com/en/geoip2-anonymous-ip-database GeoIP2>`_
|
|
||||||
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)
|
|
@@ -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)
|
|
Reference in New Issue
Block a user