Permalink
Browse files

[soc2009/multidb] Merged up to trunk r11009

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11011 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 83df1f1 commit f6db1dd357d22d9a376b909c169894a8bba0a49c @alex alex committed Jun 16, 2009
@@ -317,7 +317,7 @@ def get_default_prefix(cls):
def get_queryset(self):
# Avoid a circular import.
from django.contrib.contenttypes.models import ContentType
- if self.instance is None:
+ if self.instance is None or self.instance.pk is None:
return self.model._default_manager.none()
return self.model._default_manager.filter(**{
self.ct_field.name: ContentType.objects.get_for_model(self.instance),
@@ -14,7 +14,7 @@
lib_names = None
elif os.name == 'nt':
# Windows NT shared library
- lib_names = ['gdal15']
+ lib_names = ['gdal16', 'gdal15']
elif os.name == 'posix':
# *NIX library names.
lib_names = ['gdal', 'GDAL', 'gdal1.6.0', 'gdal1.5.0', 'gdal1.4.0']
@@ -84,16 +84,15 @@ def test04_city(self):
self.assertEqual('USA', d['country_code3'])
self.assertEqual('Houston', d['city'])
self.assertEqual('TX', d['region'])
- self.assertEqual('77002', d['postal_code'])
self.assertEqual(713, d['area_code'])
geom = g.geos(query)
self.failIf(not isinstance(geom, GEOSGeometry))
- lon, lat = (-95.366996765, 29.752300262)
+ lon, lat = (-95.4152, 29.7755)
lat_lon = g.lat_lon(query)
lat_lon = (lat_lon[1], lat_lon[0])
for tup in (geom.tuple, g.coords(query), g.lon_lat(query), lat_lon):
- self.assertAlmostEqual(lon, tup[0], 9)
- self.assertAlmostEqual(lat, tup[1], 9)
+ self.assertAlmostEqual(lon, tup[0], 4)
+ self.assertAlmostEqual(lat, tup[1], 4)
def suite():
s = unittest.TestSuite()
@@ -6,7 +6,7 @@
GeoIP(R) is a registered trademark of MaxMind, LLC of Boston, Massachusetts.
For IP-based geolocation, this module requires the GeoLite Country and City
- datasets, in binary format (CSV will not work!). The datasets may be
+ datasets, in binary format (CSV will not work!). The datasets may be
downloaded from MaxMind at http://www.maxmind.com/download/geoip/database/.
Grab GeoIP.dat.gz and GeoLiteCity.dat.gz, and unzip them in the directory
corresponding to settings.GEOIP_PATH. See the GeoIP docstring and examples
@@ -34,7 +34,7 @@
>>> g.lat_lon('salon.com')
(37.789798736572266, -122.39420318603516)
>>> g.lon_lat('uh.edu')
- (-95.415199279785156, 29.77549934387207)
+ (-95.415199279785156, 29.77549934387207)
>>> g.geos('24.124.1.80').wkt
'POINT (-95.2087020874023438 39.0392990112304688)'
"""
@@ -45,7 +45,7 @@
if not settings.configured: settings.configure()
# Creating the settings dictionary with any settings, if needed.
-GEOIP_SETTINGS = dict((key, getattr(settings, key))
+GEOIP_SETTINGS = dict((key, getattr(settings, key))
for key in ('GEOIP_PATH', 'GEOIP_LIBRARY_PATH', 'GEOIP_COUNTRY', 'GEOIP_CITY')
if hasattr(settings, key))
lib_path = GEOIP_SETTINGS.get('GEOIP_LIBRARY_PATH', None)
@@ -83,8 +83,17 @@ class GeoIPRecord(Structure):
('postal_code', c_char_p),
('latitude', c_float),
('longitude', c_float),
+ # TODO: In 1.4.6 this changed from `int dma_code;` to
+ # `union {int metro_code; int dma_code;};`. Change
+ # to a `ctypes.Union` in to accomodate in future when
+ # pre-1.4.6 versions are no longer distributed.
('dma_code', c_int),
('area_code', c_int),
+ # TODO: The following structure fields were added in 1.4.3 --
+ # uncomment these fields when sure previous versions are no
+ # longer distributed by package maintainers.
+ #('charset', c_int),
+ #('continent_code', c_char_p),
]
class GeoIPTag(Structure): pass
@@ -99,9 +108,12 @@ def record_output(func):
rec_by_addr = record_output(lgeoip.GeoIP_record_by_addr)
rec_by_name = record_output(lgeoip.GeoIP_record_by_name)
-# For opening up GeoIP databases.
+# For opening & closing GeoIP database files.
geoip_open = lgeoip.GeoIP_open
geoip_open.restype = DBTYPE
+geoip_close = lgeoip.GeoIP_delete
+geoip_close.argtypes = [DBTYPE]
+geoip_close.restype = None
# String output routines.
def string_output(func):
@@ -136,6 +148,12 @@ class GeoIP(object):
GEOIP_CHECK_CACHE = 2
GEOIP_INDEX_CACHE = 4
cache_options = dict((opt, None) for opt in (0, 1, 2, 4))
+ _city_file = ''
+ _country_file = ''
+
+ # Initially, pointers to GeoIP file references are NULL.
+ _city = None
+ _country = None
def __init__(self, path=None, cache=0, country=None, city=None):
"""
@@ -174,43 +192,42 @@ def __init__(self, path=None, cache=0, country=None, city=None):
if not isinstance(path, basestring):
raise TypeError('Invalid path type: %s' % type(path).__name__)
- cntry_ptr, city_ptr = (None, None)
if os.path.isdir(path):
- # Getting the country and city files using the settings
- # dictionary. If no settings are provided, default names
- # are assigned.
- country = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat'))
- city = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat'))
+ # Constructing the GeoIP database filenames using the settings
+ # dictionary. If the database files for the GeoLite country
+ # and/or city datasets exist, then try and open them.
+ country_db = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat'))
+ if os.path.isfile(country_db):
+ self._country = geoip_open(country_db, cache)
+ self._country_file = country_db
+
+ city_db = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat'))
+ if os.path.isfile(city_db):
+ self._city = geoip_open(city_db, cache)
+ self._city_file = city_db
elif os.path.isfile(path):
# Otherwise, some detective work will be needed to figure
# out whether the given database path is for the GeoIP country
# or city databases.
ptr = geoip_open(path, cache)
info = geoip_dbinfo(ptr)
if lite_regex.match(info):
- # GeoLite City database.
- city, city_ptr = path, ptr
+ # GeoLite City database detected.
+ self._city = ptr
+ self._city_file = path
elif free_regex.match(info):
- # GeoIP Country database.
- country, cntry_ptr = path, ptr
+ # GeoIP Country database detected.
+ self._country = ptr
+ self._country_file = path
else:
raise GeoIPException('Unable to recognize database edition: %s' % info)
else:
raise GeoIPException('GeoIP path must be a valid file or directory.')
-
- # `_init_db` does the dirty work.
- self._init_db(country, cache, '_country', cntry_ptr)
- self._init_db(city, cache, '_city', city_ptr)
-
- def _init_db(self, db_file, cache, attname, ptr=None):
- "Helper routine for setting GeoIP ctypes database properties."
- if ptr:
- # Pointer already retrieved.
- pass
- elif os.path.isfile(db_file or ''):
- ptr = geoip_open(db_file, cache)
- setattr(self, attname, ptr)
- setattr(self, '%s_file' % attname, db_file)
+
+ def __del__(self):
+ # Cleaning any GeoIP file handles lying around.
+ if self._country: geoip_close(self._country)
+ if self._city: geoip_close(self._city)
def _check_query(self, query, country=False, city=False, city_or_country=False):
"Helper routine for checking the query and database availability."
@@ -219,11 +236,11 @@ def _check_query(self, query, country=False, city=False, city_or_country=False):
raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__)
# Extra checks for the existence of country and city databases.
- if city_or_country and self._country is None and self._city is None:
+ if city_or_country and not (self._country or self._city):
raise GeoIPException('Invalid GeoIP country and city data files.')
- elif country and self._country is None:
+ elif country and not self._country:
raise GeoIPException('Invalid GeoIP country data file: %s' % self._country_file)
- elif city and self._city is None:
+ elif city and not self._city:
raise GeoIPException('Invalid GeoIP city data file: %s' % self._city_file)
def city(self, query):
@@ -247,7 +264,7 @@ def city(self, query):
return dict((tup[0], getattr(record, tup[0])) for tup in record._fields_)
else:
return None
-
+
def country_code(self, query):
"Returns the country code for the given IP Address or FQDN."
self._check_query(query, city_or_country=True)
@@ -268,12 +285,12 @@ def country_name(self, query):
def country(self, query):
"""
- Returns a dictonary with with the country code and name when given an
+ Returns a dictonary with with the country code and name when given an
IP address or a Fully Qualified Domain Name (FQDN). For example, both
'24.124.1.80' and 'djangoproject.com' are valid parameters.
"""
# Returning the country code and name
- return {'country_code' : self.country_code(query),
+ return {'country_code' : self.country_code(query),
'country_name' : self.country_name(query),
}
@@ -318,7 +335,7 @@ def city_info(self):
ci = geoip_dbinfo(self._city)
return ci
city_info = property(city_info)
-
+
def info(self):
"Returns information about all GeoIP databases in use."
return 'Country:\n\t%s\nCity:\n\t%s' % (self.country_info, self.city_info)
View
@@ -195,7 +195,7 @@ class EmailMessage(object):
A container for email information.
"""
content_subtype = 'plain'
- multipart_subtype = 'mixed'
+ mixed_subtype = 'mixed'
encoding = None # None => use settings default
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
@@ -234,16 +234,7 @@ def message(self):
encoding = self.encoding or settings.DEFAULT_CHARSET
msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET),
self.content_subtype, encoding)
- if self.attachments:
- body_msg = msg
- msg = SafeMIMEMultipart(_subtype=self.multipart_subtype)
- if self.body:
- msg.attach(body_msg)
- for attachment in self.attachments:
- if isinstance(attachment, MIMEBase):
- msg.attach(attachment)
- else:
- msg.attach(self._create_attachment(*attachment))
+ msg = self._create_message(msg)
msg['Subject'] = self.subject
msg['From'] = self.extra_headers.pop('From', self.from_email)
msg['To'] = ', '.join(self.to)
@@ -277,8 +268,7 @@ def send(self, fail_silently=False):
def attach(self, filename=None, content=None, mimetype=None):
"""
Attaches a file with the given filename and content. The filename can
- be omitted (useful for multipart/alternative messages) and the mimetype
- is guessed, if not provided.
+ be omitted and the mimetype is guessed, if not provided.
If the first parameter is a MIMEBase subclass it is inserted directly
into the resulting message attachments.
@@ -296,15 +286,26 @@ def attach_file(self, path, mimetype=None):
content = open(path, 'rb').read()
self.attach(filename, content, mimetype)
- def _create_attachment(self, filename, content, mimetype=None):
+ def _create_message(self, msg):
+ return self._create_attachments(msg)
+
+ def _create_attachments(self, msg):
+ if self.attachments:
+ body_msg = msg
+ msg = SafeMIMEMultipart(_subtype=self.mixed_subtype)
+ if self.body:
+ msg.attach(body_msg)
+ for attachment in self.attachments:
+ if isinstance(attachment, MIMEBase):
+ msg.attach(attachment)
+ else:
+ msg.attach(self._create_attachment(*attachment))
+ return msg
+
+ def _create_mime_attachment(self, content, mimetype):
"""
- Converts the filename, content, mimetype triple into a MIME attachment
- object.
+ Converts the content, mimetype pair into a MIME attachment object.
"""
- if mimetype is None:
- mimetype, _ = mimetypes.guess_type(filename)
- if mimetype is None:
- mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
basetype, subtype = mimetype.split('/', 1)
if basetype == 'text':
attachment = SafeMIMEText(smart_str(content,
@@ -314,6 +315,18 @@ def _create_attachment(self, filename, content, mimetype=None):
attachment = MIMEBase(basetype, subtype)
attachment.set_payload(content)
Encoders.encode_base64(attachment)
+ return attachment
+
+ def _create_attachment(self, filename, content, mimetype=None):
+ """
+ Converts the filename, content, mimetype triple into a MIME attachment
+ object.
+ """
+ if mimetype is None:
+ mimetype, _ = mimetypes.guess_type(filename)
+ if mimetype is None:
+ mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
+ attachment = self._create_mime_attachment(content, mimetype)
if filename:
attachment.add_header('Content-Disposition', 'attachment',
filename=filename)
@@ -325,11 +338,39 @@ class EmailMultiAlternatives(EmailMessage):
messages. For example, including text and HTML versions of the text is
made easier.
"""
- multipart_subtype = 'alternative'
+ alternative_subtype = 'alternative'
- def attach_alternative(self, content, mimetype=None):
+ def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
+ connection=None, attachments=None, headers=None, alternatives=None):
+ """
+ Initialize a single email message (which can be sent to multiple
+ recipients).
+
+ All strings used to create the message can be unicode strings (or UTF-8
+ bytestrings). The SafeMIMEText class will handle any necessary encoding
+ conversions.
+ """
+ super(EmailMultiAlternatives, self).__init__(subject, body, from_email, to, bcc, connection, attachments, headers)
+ self.alternatives=alternatives or []
+
+ def attach_alternative(self, content, mimetype):
"""Attach an alternative content representation."""
- self.attach(content=content, mimetype=mimetype)
+ assert content is not None
+ assert mimetype is not None
+ self.alternatives.append((content, mimetype))
+
+ def _create_message(self, msg):
+ return self._create_attachments(self._create_alternatives(msg))
+
+ def _create_alternatives(self, msg):
+ if self.alternatives:
+ body_msg = msg
+ msg = SafeMIMEMultipart(_subtype=self.alternative_subtype)
+ if self.body:
+ msg.attach(body_msg)
+ for alternative in self.alternatives:
+ msg.attach(self._create_mime_attachment(*alternative))
+ return msg
def send_mail(subject, message, from_email, recipient_list,
fail_silently=False, auth_user=None, auth_password=None):
@@ -26,8 +26,11 @@ def __init__(self, connection):
self.connection = connection
def _digest(self, *args):
- "Generate a 32 bit digest of a set of arguments that can be used to shorten identifying names"
- return '%x' % (abs(hash(args)) % (1<<32))
+ """
+ Generates a 32-bit digest of a set of arguments that can be used to
+ shorten identifying names.
+ """
+ return '%x' % (abs(hash(args)) % 4294967296L) # 2**32
def sql_create_model(self, model, style, known_models=set()):
"""
Oops, something went wrong.

0 comments on commit f6db1dd

Please sign in to comment.