149 lines
4.2 KiB
Python
149 lines
4.2 KiB
Python
|
import requests
|
||
|
from ua_parser import user_agent_parser
|
||
|
|
||
|
_SUPPORT_DATA = requests.get('https://raw.githubusercontent.com/Fyrd/caniuse/master/data.json').json()['data']
|
||
|
|
||
|
SUPPORT = 'y'
|
||
|
PARTIAL_SUPPORT = 'a'
|
||
|
UNSUPPORTED = 'n'
|
||
|
POLYFILL = 'p'
|
||
|
UNKNOWN = 'u'
|
||
|
PREFIX = 'x'
|
||
|
DISABLED = 'd'
|
||
|
|
||
|
|
||
|
def safe_int(string):
|
||
|
try:
|
||
|
return int(string)
|
||
|
except (ValueError, TypeError):
|
||
|
return 0
|
||
|
|
||
|
|
||
|
class BrowserFamily(object):
|
||
|
def __init__(self, data):
|
||
|
self._data = data
|
||
|
self._ranges = ranges = []
|
||
|
self._versions = versions = {}
|
||
|
max_version = ()
|
||
|
max_support = UNKNOWN
|
||
|
|
||
|
for version, support in data.items():
|
||
|
if version == 'all':
|
||
|
self.max_support = support
|
||
|
elif '-' in version:
|
||
|
start, end = version.split('-')
|
||
|
start = tuple(map(int, start.split('.')))
|
||
|
end = tuple(map(int, end.split('.'))) + (1e3000,)
|
||
|
ranges.append((start, end, support))
|
||
|
if end > max_version:
|
||
|
max_version = end
|
||
|
max_support = support
|
||
|
else:
|
||
|
try:
|
||
|
version = tuple(map(int, version.split('.')))
|
||
|
except ValueError:
|
||
|
pass
|
||
|
else:
|
||
|
if version > max_version:
|
||
|
max_version = version
|
||
|
max_support = support
|
||
|
versions[version] = support
|
||
|
|
||
|
self.max_version = max_version
|
||
|
self.max_support = max_support
|
||
|
|
||
|
def check(self, major, minor, patch):
|
||
|
int_major, int_minor, int_patch = map(safe_int, (major, minor, patch))
|
||
|
|
||
|
version = (int_major, int_minor, int_patch)
|
||
|
if version > self.max_version:
|
||
|
return self.max_support
|
||
|
|
||
|
for key in ((int_major, int_minor, int_patch), (int_major, int_minor), (int_major,), major):
|
||
|
try:
|
||
|
return self._versions[key]
|
||
|
except KeyError:
|
||
|
pass
|
||
|
|
||
|
for start, end, support in self._ranges:
|
||
|
if start <= version < end:
|
||
|
return support
|
||
|
|
||
|
return UNKNOWN
|
||
|
|
||
|
|
||
|
class Feat(object):
|
||
|
def __init__(self, data):
|
||
|
self._data = data
|
||
|
self._family = {name: BrowserFamily(data) for name, data in data['stats'].items()}
|
||
|
|
||
|
def __getitem__(self, item):
|
||
|
return self._family[item]
|
||
|
|
||
|
|
||
|
class Database(object):
|
||
|
def __init__(self, data):
|
||
|
self._data = data
|
||
|
self._feats = {feat: Feat(data) for feat, data in data.items()}
|
||
|
|
||
|
def __getitem__(self, item):
|
||
|
return self._feats[item]
|
||
|
|
||
|
|
||
|
database = Database(_SUPPORT_DATA)
|
||
|
|
||
|
|
||
|
class CanIUse(object):
|
||
|
def __init__(self, ua):
|
||
|
self._agent = user_agent_parser.Parse(ua)
|
||
|
|
||
|
os_family = self._agent['os']['family']
|
||
|
browser_family = self._agent['user_agent']['family']
|
||
|
|
||
|
family = None
|
||
|
|
||
|
if os_family == 'Android':
|
||
|
if 'Firefox' in browser_family:
|
||
|
family = 'and_ff'
|
||
|
elif 'Chrome' in browser_family:
|
||
|
family = 'and_chr'
|
||
|
elif 'Android' in browser_family:
|
||
|
family = 'android'
|
||
|
else:
|
||
|
if 'Edge' in browser_family:
|
||
|
family = 'edge'
|
||
|
elif 'Firefox' in browser_family:
|
||
|
family = 'firefox'
|
||
|
elif 'Chrome' in browser_family:
|
||
|
family = 'chrome'
|
||
|
elif 'IE' in browser_family:
|
||
|
family = 'ie'
|
||
|
elif 'Opera' in browser_family:
|
||
|
family = 'opera'
|
||
|
elif 'Safari' in browser_family:
|
||
|
family = 'safari'
|
||
|
|
||
|
self._family = family
|
||
|
|
||
|
def _check_feat(self, feat):
|
||
|
if not self._family:
|
||
|
return UNKNOWN
|
||
|
|
||
|
try:
|
||
|
stats = feat[self._family]
|
||
|
except KeyError:
|
||
|
return UNKNOWN
|
||
|
else:
|
||
|
ua = self._agent['user_agent']
|
||
|
return stats.check(ua['major'], ua['minor'], ua['patch'])[0]
|
||
|
|
||
|
def __getattr__(self, attr):
|
||
|
try:
|
||
|
feat = database[attr.replace('_', '-')]
|
||
|
except KeyError:
|
||
|
raise AttributeError(attr)
|
||
|
else:
|
||
|
result = self._check_feat(feat)
|
||
|
setattr(self, attr, result)
|
||
|
return result
|