Skip to content

Commit c435676

Browse files
committed
[soc2009/http-wsgi-improvements] Merged up to 11009 from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/http-wsgi-improvements@11013 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 3d4f9ec commit c435676

File tree

17 files changed

+303
-115
lines changed

17 files changed

+303
-115
lines changed

django/contrib/contenttypes/generic.py

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -317,7 +317,7 @@ def get_default_prefix(cls):
317
def get_queryset(self):
317
def get_queryset(self):
318
# Avoid a circular import.
318
# Avoid a circular import.
319
from django.contrib.contenttypes.models import ContentType
319
from django.contrib.contenttypes.models import ContentType
320-
if self.instance is None:
320+
if self.instance is None or self.instance.pk is None:
321
return self.model._default_manager.none()
321
return self.model._default_manager.none()
322
return self.model._default_manager.filter(**{
322
return self.model._default_manager.filter(**{
323
self.ct_field.name: ContentType.objects.get_for_model(self.instance),
323
self.ct_field.name: ContentType.objects.get_for_model(self.instance),

django/contrib/gis/gdal/libgdal.py

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -14,7 +14,7 @@
14
lib_names = None
14
lib_names = None
15
elif os.name == 'nt':
15
elif os.name == 'nt':
16
# Windows NT shared library
16
# Windows NT shared library
17-
lib_names = ['gdal15']
17+
lib_names = ['gdal16', 'gdal15']
18
elif os.name == 'posix':
18
elif os.name == 'posix':
19
# *NIX library names.
19
# *NIX library names.
20
lib_names = ['gdal', 'GDAL', 'gdal1.6.0', 'gdal1.5.0', 'gdal1.4.0']
20
lib_names = ['gdal', 'GDAL', 'gdal1.6.0', 'gdal1.5.0', 'gdal1.4.0']

django/contrib/gis/tests/test_geoip.py

Lines changed: 3 additions & 4 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -84,16 +84,15 @@ def test04_city(self):
84
self.assertEqual('USA', d['country_code3'])
84
self.assertEqual('USA', d['country_code3'])
85
self.assertEqual('Houston', d['city'])
85
self.assertEqual('Houston', d['city'])
86
self.assertEqual('TX', d['region'])
86
self.assertEqual('TX', d['region'])
87-
self.assertEqual('77002', d['postal_code'])
88
self.assertEqual(713, d['area_code'])
87
self.assertEqual(713, d['area_code'])
89
geom = g.geos(query)
88
geom = g.geos(query)
90
self.failIf(not isinstance(geom, GEOSGeometry))
89
self.failIf(not isinstance(geom, GEOSGeometry))
91-
lon, lat = (-95.366996765, 29.752300262)
90+
lon, lat = (-95.4152, 29.7755)
92
lat_lon = g.lat_lon(query)
91
lat_lon = g.lat_lon(query)
93
lat_lon = (lat_lon[1], lat_lon[0])
92
lat_lon = (lat_lon[1], lat_lon[0])
94
for tup in (geom.tuple, g.coords(query), g.lon_lat(query), lat_lon):
93
for tup in (geom.tuple, g.coords(query), g.lon_lat(query), lat_lon):
95-
self.assertAlmostEqual(lon, tup[0], 9)
94+
self.assertAlmostEqual(lon, tup[0], 4)
96-
self.assertAlmostEqual(lat, tup[1], 9)
95+
self.assertAlmostEqual(lat, tup[1], 4)
97

96

98
def suite():
97
def suite():
99
s = unittest.TestSuite()
98
s = unittest.TestSuite()

django/contrib/gis/utils/geoip.py

Lines changed: 52 additions & 35 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -6,7 +6,7 @@
6
GeoIP(R) is a registered trademark of MaxMind, LLC of Boston, Massachusetts.
6
GeoIP(R) is a registered trademark of MaxMind, LLC of Boston, Massachusetts.
7
7
8
For IP-based geolocation, this module requires the GeoLite Country and City
8
For IP-based geolocation, this module requires the GeoLite Country and City
9-
datasets, in binary format (CSV will not work!). The datasets may be
9+
datasets, in binary format (CSV will not work!). The datasets may be
10
downloaded from MaxMind at http://www.maxmind.com/download/geoip/database/.
10
downloaded from MaxMind at http://www.maxmind.com/download/geoip/database/.
11
Grab GeoIP.dat.gz and GeoLiteCity.dat.gz, and unzip them in the directory
11
Grab GeoIP.dat.gz and GeoLiteCity.dat.gz, and unzip them in the directory
12
corresponding to settings.GEOIP_PATH. See the GeoIP docstring and examples
12
corresponding to settings.GEOIP_PATH. See the GeoIP docstring and examples
@@ -34,7 +34,7 @@
34
>>> g.lat_lon('salon.com')
34
>>> g.lat_lon('salon.com')
35
(37.789798736572266, -122.39420318603516)
35
(37.789798736572266, -122.39420318603516)
36
>>> g.lon_lat('uh.edu')
36
>>> g.lon_lat('uh.edu')
37-
(-95.415199279785156, 29.77549934387207)
37+
(-95.415199279785156, 29.77549934387207)
38
>>> g.geos('24.124.1.80').wkt
38
>>> g.geos('24.124.1.80').wkt
39
'POINT (-95.2087020874023438 39.0392990112304688)'
39
'POINT (-95.2087020874023438 39.0392990112304688)'
40
"""
40
"""
@@ -45,7 +45,7 @@
45
if not settings.configured: settings.configure()
45
if not settings.configured: settings.configure()
46

46

47
# Creating the settings dictionary with any settings, if needed.
47
# Creating the settings dictionary with any settings, if needed.
48-
GEOIP_SETTINGS = dict((key, getattr(settings, key))
48+
GEOIP_SETTINGS = dict((key, getattr(settings, key))
49
for key in ('GEOIP_PATH', 'GEOIP_LIBRARY_PATH', 'GEOIP_COUNTRY', 'GEOIP_CITY')
49
for key in ('GEOIP_PATH', 'GEOIP_LIBRARY_PATH', 'GEOIP_COUNTRY', 'GEOIP_CITY')
50
if hasattr(settings, key))
50
if hasattr(settings, key))
51
lib_path = GEOIP_SETTINGS.get('GEOIP_LIBRARY_PATH', None)
51
lib_path = GEOIP_SETTINGS.get('GEOIP_LIBRARY_PATH', None)
@@ -83,8 +83,17 @@ class GeoIPRecord(Structure):
83
('postal_code', c_char_p),
83
('postal_code', c_char_p),
84
('latitude', c_float),
84
('latitude', c_float),
85
('longitude', c_float),
85
('longitude', c_float),
86+
# TODO: In 1.4.6 this changed from `int dma_code;` to
87+
# `union {int metro_code; int dma_code;};`. Change
88+
# to a `ctypes.Union` in to accomodate in future when
89+
# pre-1.4.6 versions are no longer distributed.
86
('dma_code', c_int),
90
('dma_code', c_int),
87
('area_code', c_int),
91
('area_code', c_int),
92+
# TODO: The following structure fields were added in 1.4.3 --
93+
# uncomment these fields when sure previous versions are no
94+
# longer distributed by package maintainers.
95+
#('charset', c_int),
96+
#('continent_code', c_char_p),
88
]
97
]
89
class GeoIPTag(Structure): pass
98
class GeoIPTag(Structure): pass
90

99

@@ -99,9 +108,12 @@ def record_output(func):
99
rec_by_addr = record_output(lgeoip.GeoIP_record_by_addr)
108
rec_by_addr = record_output(lgeoip.GeoIP_record_by_addr)
100
rec_by_name = record_output(lgeoip.GeoIP_record_by_name)
109
rec_by_name = record_output(lgeoip.GeoIP_record_by_name)
101

110

102-
# For opening up GeoIP databases.
111+
# For opening & closing GeoIP database files.
103
geoip_open = lgeoip.GeoIP_open
112
geoip_open = lgeoip.GeoIP_open
104
geoip_open.restype = DBTYPE
113
geoip_open.restype = DBTYPE
114+
geoip_close = lgeoip.GeoIP_delete
115+
geoip_close.argtypes = [DBTYPE]
116+
geoip_close.restype = None
105

117

106
# String output routines.
118
# String output routines.
107
def string_output(func):
119
def string_output(func):
@@ -136,6 +148,12 @@ class GeoIP(object):
136
GEOIP_CHECK_CACHE = 2
148
GEOIP_CHECK_CACHE = 2
137
GEOIP_INDEX_CACHE = 4
149
GEOIP_INDEX_CACHE = 4
138
cache_options = dict((opt, None) for opt in (0, 1, 2, 4))
150
cache_options = dict((opt, None) for opt in (0, 1, 2, 4))
151+
_city_file = ''
152+
_country_file = ''
153+
154+
# Initially, pointers to GeoIP file references are NULL.
155+
_city = None
156+
_country = None
139

157

140
def __init__(self, path=None, cache=0, country=None, city=None):
158
def __init__(self, path=None, cache=0, country=None, city=None):
141
"""
159
"""
@@ -174,43 +192,42 @@ def __init__(self, path=None, cache=0, country=None, city=None):
174
if not isinstance(path, basestring):
192
if not isinstance(path, basestring):
175
raise TypeError('Invalid path type: %s' % type(path).__name__)
193
raise TypeError('Invalid path type: %s' % type(path).__name__)
176

194

177-
cntry_ptr, city_ptr = (None, None)
178
if os.path.isdir(path):
195
if os.path.isdir(path):
179-
# Getting the country and city files using the settings
196+
# Constructing the GeoIP database filenames using the settings
180-
# dictionary. If no settings are provided, default names
197+
# dictionary. If the database files for the GeoLite country
181-
# are assigned.
198+
# and/or city datasets exist, then try and open them.
182-
country = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat'))
199+
country_db = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat'))
183-
city = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat'))
200+
if os.path.isfile(country_db):
201+
self._country = geoip_open(country_db, cache)
202+
self._country_file = country_db
203+
204+
city_db = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat'))
205+
if os.path.isfile(city_db):
206+
self._city = geoip_open(city_db, cache)
207+
self._city_file = city_db
184
elif os.path.isfile(path):
208
elif os.path.isfile(path):
185
# Otherwise, some detective work will be needed to figure
209
# Otherwise, some detective work will be needed to figure
186
# out whether the given database path is for the GeoIP country
210
# out whether the given database path is for the GeoIP country
187
# or city databases.
211
# or city databases.
188
ptr = geoip_open(path, cache)
212
ptr = geoip_open(path, cache)
189
info = geoip_dbinfo(ptr)
213
info = geoip_dbinfo(ptr)
190
if lite_regex.match(info):
214
if lite_regex.match(info):
191-
# GeoLite City database.
215+
# GeoLite City database detected.
192-
city, city_ptr = path, ptr
216+
self._city = ptr
217+
self._city_file = path
193
elif free_regex.match(info):
218
elif free_regex.match(info):
194-
# GeoIP Country database.
219+
# GeoIP Country database detected.
195-
country, cntry_ptr = path, ptr
220+
self._country = ptr
221+
self._country_file = path
196
else:
222
else:
197
raise GeoIPException('Unable to recognize database edition: %s' % info)
223
raise GeoIPException('Unable to recognize database edition: %s' % info)
198
else:
224
else:
199
raise GeoIPException('GeoIP path must be a valid file or directory.')
225
raise GeoIPException('GeoIP path must be a valid file or directory.')
200-
226+
201-
# `_init_db` does the dirty work.
227+
def __del__(self):
202-
self._init_db(country, cache, '_country', cntry_ptr)
228+
# Cleaning any GeoIP file handles lying around.
203-
self._init_db(city, cache, '_city', city_ptr)
229+
if self._country: geoip_close(self._country)
204-
230+
if self._city: geoip_close(self._city)
205-
def _init_db(self, db_file, cache, attname, ptr=None):
206-
"Helper routine for setting GeoIP ctypes database properties."
207-
if ptr:
208-
# Pointer already retrieved.
209-
pass
210-
elif os.path.isfile(db_file or ''):
211-
ptr = geoip_open(db_file, cache)
212-
setattr(self, attname, ptr)
213-
setattr(self, '%s_file' % attname, db_file)
214

231

215
def _check_query(self, query, country=False, city=False, city_or_country=False):
232
def _check_query(self, query, country=False, city=False, city_or_country=False):
216
"Helper routine for checking the query and database availability."
233
"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):
219
raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__)
236
raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__)
220

237

221
# Extra checks for the existence of country and city databases.
238
# Extra checks for the existence of country and city databases.
222-
if city_or_country and self._country is None and self._city is None:
239+
if city_or_country and not (self._country or self._city):
223
raise GeoIPException('Invalid GeoIP country and city data files.')
240
raise GeoIPException('Invalid GeoIP country and city data files.')
224-
elif country and self._country is None:
241+
elif country and not self._country:
225
raise GeoIPException('Invalid GeoIP country data file: %s' % self._country_file)
242
raise GeoIPException('Invalid GeoIP country data file: %s' % self._country_file)
226-
elif city and self._city is None:
243+
elif city and not self._city:
227
raise GeoIPException('Invalid GeoIP city data file: %s' % self._city_file)
244
raise GeoIPException('Invalid GeoIP city data file: %s' % self._city_file)
228

245

229
def city(self, query):
246
def city(self, query):
@@ -247,7 +264,7 @@ def city(self, query):
247
return dict((tup[0], getattr(record, tup[0])) for tup in record._fields_)
264
return dict((tup[0], getattr(record, tup[0])) for tup in record._fields_)
248
else:
265
else:
249
return None
266
return None
250-
267+
251
def country_code(self, query):
268
def country_code(self, query):
252
"Returns the country code for the given IP Address or FQDN."
269
"Returns the country code for the given IP Address or FQDN."
253
self._check_query(query, city_or_country=True)
270
self._check_query(query, city_or_country=True)
@@ -268,12 +285,12 @@ def country_name(self, query):
268

285

269
def country(self, query):
286
def country(self, query):
270
"""
287
"""
271-
Returns a dictonary with with the country code and name when given an
288+
Returns a dictonary with with the country code and name when given an
272
IP address or a Fully Qualified Domain Name (FQDN). For example, both
289
IP address or a Fully Qualified Domain Name (FQDN). For example, both
273
'24.124.1.80' and 'djangoproject.com' are valid parameters.
290
'24.124.1.80' and 'djangoproject.com' are valid parameters.
274
"""
291
"""
275
# Returning the country code and name
292
# Returning the country code and name
276-
return {'country_code' : self.country_code(query),
293+
return {'country_code' : self.country_code(query),
277
'country_name' : self.country_name(query),
294
'country_name' : self.country_name(query),
278
}
295
}
279

296

@@ -318,7 +335,7 @@ def city_info(self):
318
ci = geoip_dbinfo(self._city)
335
ci = geoip_dbinfo(self._city)
319
return ci
336
return ci
320
city_info = property(city_info)
337
city_info = property(city_info)
321-
338+
322
def info(self):
339
def info(self):
323
"Returns information about all GeoIP databases in use."
340
"Returns information about all GeoIP databases in use."
324
return 'Country:\n\t%s\nCity:\n\t%s' % (self.country_info, self.city_info)
341
return 'Country:\n\t%s\nCity:\n\t%s' % (self.country_info, self.city_info)

django/core/mail.py

Lines changed: 64 additions & 23 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -195,7 +195,7 @@ class EmailMessage(object):
195
A container for email information.
195
A container for email information.
196
"""
196
"""
197
content_subtype = 'plain'
197
content_subtype = 'plain'
198-
multipart_subtype = 'mixed'
198+
mixed_subtype = 'mixed'
199
encoding = None # None => use settings default
199
encoding = None # None => use settings default
200

200

201
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
201
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
@@ -234,16 +234,7 @@ def message(self):
234
encoding = self.encoding or settings.DEFAULT_CHARSET
234
encoding = self.encoding or settings.DEFAULT_CHARSET
235
msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET),
235
msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET),
236
self.content_subtype, encoding)
236
self.content_subtype, encoding)
237-
if self.attachments:
237+
msg = self._create_message(msg)
238-
body_msg = msg
239-
msg = SafeMIMEMultipart(_subtype=self.multipart_subtype)
240-
if self.body:
241-
msg.attach(body_msg)
242-
for attachment in self.attachments:
243-
if isinstance(attachment, MIMEBase):
244-
msg.attach(attachment)
245-
else:
246-
msg.attach(self._create_attachment(*attachment))
247
msg['Subject'] = self.subject
238
msg['Subject'] = self.subject
248
msg['From'] = self.extra_headers.pop('From', self.from_email)
239
msg['From'] = self.extra_headers.pop('From', self.from_email)
249
msg['To'] = ', '.join(self.to)
240
msg['To'] = ', '.join(self.to)
@@ -277,8 +268,7 @@ def send(self, fail_silently=False):
277
def attach(self, filename=None, content=None, mimetype=None):
268
def attach(self, filename=None, content=None, mimetype=None):
278
"""
269
"""
279
Attaches a file with the given filename and content. The filename can
270
Attaches a file with the given filename and content. The filename can
280-
be omitted (useful for multipart/alternative messages) and the mimetype
271+
be omitted and the mimetype is guessed, if not provided.
281-
is guessed, if not provided.
282
272
283
If the first parameter is a MIMEBase subclass it is inserted directly
273
If the first parameter is a MIMEBase subclass it is inserted directly
284
into the resulting message attachments.
274
into the resulting message attachments.
@@ -296,15 +286,26 @@ def attach_file(self, path, mimetype=None):
296
content = open(path, 'rb').read()
286
content = open(path, 'rb').read()
297
self.attach(filename, content, mimetype)
287
self.attach(filename, content, mimetype)
298

288

299-
def _create_attachment(self, filename, content, mimetype=None):
289+
def _create_message(self, msg):
290+
return self._create_attachments(msg)
291+
292+
def _create_attachments(self, msg):
293+
if self.attachments:
294+
body_msg = msg
295+
msg = SafeMIMEMultipart(_subtype=self.mixed_subtype)
296+
if self.body:
297+
msg.attach(body_msg)
298+
for attachment in self.attachments:
299+
if isinstance(attachment, MIMEBase):
300+
msg.attach(attachment)
301+
else:
302+
msg.attach(self._create_attachment(*attachment))
303+
return msg
304+
305+
def _create_mime_attachment(self, content, mimetype):
300
"""
306
"""
301-
Converts the filename, content, mimetype triple into a MIME attachment
307+
Converts the content, mimetype pair into a MIME attachment object.
302-
object.
303
"""
308
"""
304-
if mimetype is None:
305-
mimetype, _ = mimetypes.guess_type(filename)
306-
if mimetype is None:
307-
mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
308
basetype, subtype = mimetype.split('/', 1)
309
basetype, subtype = mimetype.split('/', 1)
309
if basetype == 'text':
310
if basetype == 'text':
310
attachment = SafeMIMEText(smart_str(content,
311
attachment = SafeMIMEText(smart_str(content,
@@ -314,6 +315,18 @@ def _create_attachment(self, filename, content, mimetype=None):
314
attachment = MIMEBase(basetype, subtype)
315
attachment = MIMEBase(basetype, subtype)
315
attachment.set_payload(content)
316
attachment.set_payload(content)
316
Encoders.encode_base64(attachment)
317
Encoders.encode_base64(attachment)
318+
return attachment
319+
320+
def _create_attachment(self, filename, content, mimetype=None):
321+
"""
322+
Converts the filename, content, mimetype triple into a MIME attachment
323+
object.
324+
"""
325+
if mimetype is None:
326+
mimetype, _ = mimetypes.guess_type(filename)
327+
if mimetype is None:
328+
mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
329+
attachment = self._create_mime_attachment(content, mimetype)
317
if filename:
330
if filename:
318
attachment.add_header('Content-Disposition', 'attachment',
331
attachment.add_header('Content-Disposition', 'attachment',
319
filename=filename)
332
filename=filename)
@@ -325,11 +338,39 @@ class EmailMultiAlternatives(EmailMessage):
325
messages. For example, including text and HTML versions of the text is
338
messages. For example, including text and HTML versions of the text is
326
made easier.
339
made easier.
327
"""
340
"""
328-
multipart_subtype = 'alternative'
341+
alternative_subtype = 'alternative'
329

342

330-
def attach_alternative(self, content, mimetype=None):
343+
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
344+
connection=None, attachments=None, headers=None, alternatives=None):
345+
"""
346+
Initialize a single email message (which can be sent to multiple
347+
recipients).
348+
349+
All strings used to create the message can be unicode strings (or UTF-8
350+
bytestrings). The SafeMIMEText class will handle any necessary encoding
351+
conversions.
352+
"""
353+
super(EmailMultiAlternatives, self).__init__(subject, body, from_email, to, bcc, connection, attachments, headers)
354+
self.alternatives=alternatives or []
355+
356+
def attach_alternative(self, content, mimetype):
331
"""Attach an alternative content representation."""
357
"""Attach an alternative content representation."""
332-
self.attach(content=content, mimetype=mimetype)
358+
assert content is not None
359+
assert mimetype is not None
360+
self.alternatives.append((content, mimetype))
361+
362+
def _create_message(self, msg):
363+
return self._create_attachments(self._create_alternatives(msg))
364+
365+
def _create_alternatives(self, msg):
366+
if self.alternatives:
367+
body_msg = msg
368+
msg = SafeMIMEMultipart(_subtype=self.alternative_subtype)
369+
if self.body:
370+
msg.attach(body_msg)
371+
for alternative in self.alternatives:
372+
msg.attach(self._create_mime_attachment(*alternative))
373+
return msg
333

374

334
def send_mail(subject, message, from_email, recipient_list,
375
def send_mail(subject, message, from_email, recipient_list,
335
fail_silently=False, auth_user=None, auth_password=None):
376
fail_silently=False, auth_user=None, auth_password=None):

django/core/management/commands/dumpdata.py

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -73,7 +73,7 @@ def handle(self, *app_labels, **options):
73
model_list = get_models(app)
73
model_list = get_models(app)
74

74

75
for model in model_list:
75
for model in model_list:
76-
objects.extend(model.objects.all())
76+
objects.extend(model._default_manager.all())
77

77

78
try:
78
try:
79
return serializers.serialize(format, objects, indent=indent)
79
return serializers.serialize(format, objects, indent=indent)

django/db/backends/creation.py

Lines changed: 5 additions & 2 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -26,8 +26,11 @@ def __init__(self, connection):
26
self.connection = connection
26
self.connection = connection
27

27

28
def _digest(self, *args):
28
def _digest(self, *args):
29-
"Generate a 32 bit digest of a set of arguments that can be used to shorten identifying names"
29+
"""
30-
return '%x' % (abs(hash(args)) % (1<<32))
30+
Generates a 32-bit digest of a set of arguments that can be used to
31+
shorten identifying names.
32+
"""
33+
return '%x' % (abs(hash(args)) % 4294967296L) # 2**32
31

34

32
def sql_create_model(self, model, style, known_models=set()):
35
def sql_create_model(self, model, style, known_models=set()):
33
"""
36
"""

0 commit comments

Comments
 (0)