Google API Client Library for Python that provides discovery-based access to hundreds of Google services with authentication, caching, and media upload/download support.
—
The mimeparse module provides utilities for parsing MIME types, media ranges, and performing content negotiation. It helps applications determine the best content type to serve based on client preferences and server capabilities.
Parse MIME type strings into structured components for analysis and comparison.
def parse_mime_type(mime_type):
"""
Parse a MIME type string into its components.
Args:
mime_type (str): MIME type string (e.g., 'text/html; charset=utf-8')
Returns:
tuple: (type, subtype, params) where:
- type (str): Main type (e.g., 'text')
- subtype (str): Subtype (e.g., 'html')
- params (dict): Parameters (e.g., {'charset': 'utf-8'})
"""
def parse_media_range(range):
"""
Parse a media range string into components with quality factor.
Args:
range (str): Media range string (e.g., 'text/html;q=0.9')
Returns:
tuple: (type, subtype, params, quality) where:
- type (str): Main type
- subtype (str): Subtype
- params (dict): Parameters excluding quality
- quality (float): Quality factor (0.0 to 1.0)
"""Determine the best content type match based on client preferences and server capabilities.
def quality_parsed(mime_type_list, ranges):
"""
Calculate quality for parsed MIME types against client ranges.
Args:
mime_type_list (list): List of (type, subtype, params) tuples
ranges (list): List of parsed media ranges with quality factors
Returns:
float: Quality factor (0.0 to 1.0) for the best match
"""
def quality(mime_type, ranges):
"""
Calculate quality of a MIME type against client Accept header ranges.
Args:
mime_type (str): MIME type to evaluate (e.g., 'application/json')
ranges (str): Accept header value with media ranges and quality factors
Returns:
float: Quality factor (0.0 to 1.0) indicating preference level
"""
def best_match(supported, header):
"""
Find the best matching MIME type from supported types.
Args:
supported (list): List of MIME types supported by the server
header (str): Client Accept header value
Returns:
str: Best matching MIME type from supported list, or None if no match
"""from googleapiclient.mimeparse import parse_mime_type, parse_media_range
# Parse a simple MIME type
mime_type = "text/html"
type_, subtype, params = parse_mime_type(mime_type)
print(f"Type: {type_}, Subtype: {subtype}, Params: {params}")
# Output: Type: text, Subtype: html, Params: {}
# Parse MIME type with parameters
mime_type_with_params = "text/html; charset=utf-8; boundary=something"
type_, subtype, params = parse_mime_type(mime_type_with_params)
print(f"Type: {type_}, Subtype: {subtype}, Params: {params}")
# Output: Type: text, Subtype: html, Params: {'charset': 'utf-8', 'boundary': 'something'}
# Parse media range with quality factor
media_range = "application/json;q=0.8"
type_, subtype, params, quality = parse_media_range(media_range)
print(f"Quality: {quality}")
# Output: Quality: 0.8from googleapiclient.mimeparse import best_match, quality
# Server-supported MIME types
supported_types = [
'application/json',
'application/xml',
'text/html',
'text/plain'
]
# Client Accept headers (examples)
accept_headers = [
'application/json, application/xml;q=0.9, */*;q=0.1',
'text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8',
'application/xml, application/json;q=0.8, text/plain;q=0.5',
'*/*'
]
for accept_header in accept_headers:
best = best_match(supported_types, accept_header)
print(f"Accept: {accept_header}")
print(f"Best match: {best}")
print()
# Check quality of specific MIME type
mime_type = 'application/json'
accept_header = 'application/json;q=0.9, application/xml;q=0.8, */*;q=0.1'
quality_score = quality(mime_type, accept_header)
print(f"Quality of {mime_type}: {quality_score}")from googleapiclient.mimeparse import best_match
from googleapiclient import discovery
from flask import Flask, request
app = Flask(__name__)
class ContentNegotiator:
"""Handle content negotiation for API responses."""
def __init__(self):
self.supported_formats = {
'application/json': self._format_json,
'application/xml': self._format_xml,
'text/csv': self._format_csv,
'text/plain': self._format_plain
}
def negotiate_response_format(self, data, accept_header):
"""
Negotiate the best response format based on Accept header.
Args:
data: Response data to format
accept_header (str): Client Accept header
Returns:
tuple: (formatted_data, content_type)
"""
supported_types = list(self.supported_formats.keys())
best_type = best_match(supported_types, accept_header)
if not best_type:
# Default to JSON if no match
best_type = 'application/json'
formatter = self.supported_formats[best_type]
formatted_data = formatter(data)
return formatted_data, best_type
def _format_json(self, data):
"""Format data as JSON."""
import json
return json.dumps(data, indent=2)
def _format_xml(self, data):
"""Format data as XML."""
# Simplified XML formatting
def dict_to_xml(d, root_name='root'):
xml = f'<{root_name}>'
for key, value in d.items():
if isinstance(value, dict):
xml += dict_to_xml(value, key)
elif isinstance(value, list):
for item in value:
xml += dict_to_xml(item, key[:-1] if key.endswith('s') else key)
else:
xml += f'<{key}>{value}</{key}>'
xml += f'</{root_name}>'
return xml
return dict_to_xml(data, 'response')
def _format_csv(self, data):
"""Format data as CSV."""
import csv
import io
output = io.StringIO()
if isinstance(data, list) and data:
# Assume list of dictionaries
fieldnames = data[0].keys()
writer = csv.DictWriter(output, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)
elif isinstance(data, dict):
# Single dictionary
writer = csv.DictWriter(output, fieldnames=data.keys())
writer.writeheader()
writer.writerow(data)
return output.getvalue()
def _format_plain(self, data):
"""Format data as plain text."""
return str(data)
# Flask route with content negotiation
negotiator = ContentNegotiator()
@app.route('/api/messages')
def get_messages():
# Get messages from Gmail API
service = discovery.build('gmail', 'v1', credentials=credentials)
try:
messages_result = service.users().messages().list(
userId='me',
maxResults=10
).execute()
messages = messages_result.get('messages', [])
# Negotiate response format
accept_header = request.headers.get('Accept', 'application/json')
formatted_data, content_type = negotiator.negotiate_response_format(
messages,
accept_header
)
return formatted_data, 200, {'Content-Type': content_type}
except Exception as e:
error_data = {'error': str(e)}
accept_header = request.headers.get('Accept', 'application/json')
formatted_error, content_type = negotiator.negotiate_response_format(
error_data,
accept_header
)
return formatted_error, 500, {'Content-Type': content_type}
if __name__ == '__main__':
app.run(debug=True)from googleapiclient.mimeparse import parse_mime_type
class MimeTypeValidator:
"""Validate and analyze MIME types."""
def __init__(self):
self.allowed_types = {
'text': ['plain', 'html', 'csv', 'css', 'javascript'],
'application': ['json', 'xml', 'pdf', 'zip', 'octet-stream'],
'image': ['jpeg', 'png', 'gif', 'svg+xml'],
'audio': ['mpeg', 'wav', 'ogg'],
'video': ['mp4', 'mpeg', 'quicktime']
}
def is_valid_mime_type(self, mime_type):
"""
Check if a MIME type is valid and allowed.
Args:
mime_type (str): MIME type to validate
Returns:
tuple: (is_valid, reason)
"""
try:
type_, subtype, params = parse_mime_type(mime_type)
except Exception as e:
return False, f"Invalid MIME type format: {e}"
if type_ not in self.allowed_types:
return False, f"Type '{type_}' not allowed"
if subtype not in self.allowed_types[type_]:
return False, f"Subtype '{subtype}' not allowed for type '{type_}'"
return True, "Valid MIME type"
def get_file_categories(self, mime_types):
"""Categorize MIME types by file type."""
categories = {
'documents': [],
'images': [],
'media': [],
'data': [],
'other': []
}
for mime_type in mime_types:
try:
type_, subtype, _ = parse_mime_type(mime_type)
if type_ == 'text' or (type_ == 'application' and subtype in ['pdf', 'msword']):
categories['documents'].append(mime_type)
elif type_ == 'image':
categories['images'].append(mime_type)
elif type_ in ['audio', 'video']:
categories['media'].append(mime_type)
elif type_ == 'application' and subtype in ['json', 'xml', 'csv']:
categories['data'].append(mime_type)
else:
categories['other'].append(mime_type)
except Exception:
categories['other'].append(mime_type)
return categories
# Usage
validator = MimeTypeValidator()
test_mime_types = [
'text/plain',
'application/json',
'image/jpeg',
'invalid/mime/type',
'application/unknown'
]
for mime_type in test_mime_types:
is_valid, reason = validator.is_valid_mime_type(mime_type)
print(f"{mime_type}: {'✓' if is_valid else '✗'} - {reason}")
# Categorize file types
file_mime_types = [
'text/plain', 'application/pdf', 'image/jpeg',
'audio/mpeg', 'application/json', 'text/html'
]
categories = validator.get_file_categories(file_mime_types)
for category, types in categories.items():
if types:
print(f"{category.title()}: {', '.join(types)}")from googleapiclient.mimeparse import quality_parsed, parse_media_range
class AdvancedContentNegotiator:
"""Advanced content negotiation with custom scoring."""
def __init__(self):
self.type_preferences = {
'application/json': 1.0,
'application/xml': 0.8,
'text/html': 0.6,
'text/plain': 0.4
}
def negotiate_with_scoring(self, supported_types, accept_header):
"""
Negotiate content type with custom server preferences.
Args:
supported_types (list): MIME types supported by server
accept_header (str): Client Accept header
Returns:
tuple: (best_type, combined_score)
"""
# Parse client preferences
client_ranges = []
for range_str in accept_header.split(','):
range_str = range_str.strip()
try:
type_, subtype, params, quality = parse_media_range(range_str)
client_ranges.append((type_, subtype, params, quality))
except Exception:
continue
best_match = None
best_score = 0.0
for mime_type in supported_types:
# Parse server MIME type
try:
from googleapiclient.mimeparse import parse_mime_type
type_, subtype, params = parse_mime_type(mime_type)
server_parsed = [(type_, subtype, params)]
# Calculate client quality
client_quality = quality_parsed(server_parsed, client_ranges)
# Apply server preference
server_preference = self.type_preferences.get(mime_type, 0.5)
# Combined score
combined_score = client_quality * server_preference
if combined_score > best_score:
best_score = combined_score
best_match = mime_type
except Exception:
continue
return best_match, best_score
def get_negotiation_details(self, supported_types, accept_header):
"""Get detailed negotiation information."""
details = []
for mime_type in supported_types:
try:
from googleapiclient.mimeparse import quality
client_quality = quality(mime_type, accept_header)
server_preference = self.type_preferences.get(mime_type, 0.5)
combined_score = client_quality * server_preference
details.append({
'mime_type': mime_type,
'client_quality': client_quality,
'server_preference': server_preference,
'combined_score': combined_score
})
except Exception:
continue
# Sort by combined score
details.sort(key=lambda x: x['combined_score'], reverse=True)
return details
# Usage
negotiator = AdvancedContentNegotiator()
supported = ['application/json', 'application/xml', 'text/html', 'text/plain']
accept_header = 'text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8'
best_type, score = negotiator.negotiate_with_scoring(supported, accept_header)
print(f"Best match: {best_type} (score: {score:.3f})")
# Get detailed breakdown
details = negotiator.get_negotiation_details(supported, accept_header)
print("\nNegotiation details:")
for detail in details:
print(f" {detail['mime_type']}: "
f"client={detail['client_quality']:.2f}, "
f"server={detail['server_preference']:.2f}, "
f"combined={detail['combined_score']:.3f}")from googleapiclient.mimeparse import parse_mime_type, best_match
class MimeTypeUtils:
"""Utility functions for working with MIME types."""
@staticmethod
def is_text_type(mime_type):
"""Check if MIME type is a text type."""
try:
type_, _, _ = parse_mime_type(mime_type)
return type_ == 'text'
except Exception:
return False
@staticmethod
def is_binary_type(mime_type):
"""Check if MIME type is a binary type."""
try:
type_, subtype, _ = parse_mime_type(mime_type)
# Common binary types
binary_types = {
'image': True,
'audio': True,
'video': True,
'application': subtype not in ['json', 'xml', 'javascript', 'css']
}
return binary_types.get(type_, False)
except Exception:
return False
@staticmethod
def get_preferred_extension(mime_type):
"""Get preferred file extension for MIME type."""
extensions = {
'text/plain': '.txt',
'text/html': '.html',
'text/css': '.css',
'application/json': '.json',
'application/xml': '.xml',
'application/pdf': '.pdf',
'image/jpeg': '.jpg',
'image/png': '.png',
'image/gif': '.gif',
'audio/mpeg': '.mp3',
'video/mp4': '.mp4'
}
return extensions.get(mime_type, '')
@staticmethod
def select_encoding(mime_type):
"""Select appropriate encoding for MIME type."""
if MimeTypeUtils.is_text_type(mime_type):
return 'utf-8'
else:
return 'binary'
# Usage examples
utils = MimeTypeUtils()
test_types = [
'text/plain',
'application/json',
'image/jpeg',
'application/pdf',
'audio/mpeg'
]
for mime_type in test_types:
is_text = utils.is_text_type(mime_type)
is_binary = utils.is_binary_type(mime_type)
extension = utils.get_preferred_extension(mime_type)
encoding = utils.select_encoding(mime_type)
print(f"{mime_type}:")
print(f" Text: {is_text}, Binary: {is_binary}")
print(f" Extension: {extension}, Encoding: {encoding}")
print()Install with Tessl CLI
npx tessl i tessl/pypi-google-api-python-client@2.181.1