make_lazy_extractors.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from __future__ import unicode_literals, print_function
  2. from inspect import getsource
  3. import io
  4. import os
  5. from os.path import dirname as dirn
  6. import re
  7. import sys
  8. print('WARNING: Lazy loading extractors is an experimental feature that may not always work', file=sys.stderr)
  9. sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
  10. lazy_extractors_filename = sys.argv[1]
  11. if os.path.exists(lazy_extractors_filename):
  12. os.remove(lazy_extractors_filename)
  13. # Py2: may be confused by leftover lazy_extractors.pyc
  14. try:
  15. os.remove(lazy_extractors_filename + 'c')
  16. except OSError:
  17. pass
  18. from youtube_dl.compat import compat_register_utf8
  19. compat_register_utf8()
  20. from youtube_dl.extractor import _ALL_CLASSES
  21. from youtube_dl.extractor.common import InfoExtractor, SearchInfoExtractor
  22. with open('devscripts/lazy_load_template.py', 'rt') as f:
  23. module_template = f.read()
  24. def get_source(m):
  25. return re.sub(r'(?m)^\s*#.*\n', '', getsource(m))
  26. module_contents = [
  27. module_template,
  28. get_source(InfoExtractor.suitable),
  29. get_source(InfoExtractor._match_valid_url) + '\n',
  30. 'class LazyLoadSearchExtractor(LazyLoadExtractor):\n pass\n',
  31. # needed for suitable() methods of Youtube extractor (see #28780)
  32. 'from youtube_dl.utils import parse_qs, variadic\n',
  33. ]
  34. ie_template = '''
  35. class {name}({bases}):
  36. _VALID_URL = {valid_url!r}
  37. _module = '{module}'
  38. '''
  39. make_valid_template = '''
  40. @classmethod
  41. def _make_valid_url(cls):
  42. return {valid_url!r}
  43. '''
  44. def get_base_name(base):
  45. if base is InfoExtractor:
  46. return 'LazyLoadExtractor'
  47. elif base is SearchInfoExtractor:
  48. return 'LazyLoadSearchExtractor'
  49. else:
  50. return base.__name__
  51. def build_lazy_ie(ie, name):
  52. valid_url = getattr(ie, '_VALID_URL', None)
  53. s = ie_template.format(
  54. name=name,
  55. bases=', '.join(map(get_base_name, ie.__bases__)),
  56. valid_url=valid_url,
  57. module=ie.__module__)
  58. if ie.suitable.__func__ is not InfoExtractor.suitable.__func__:
  59. s += '\n' + get_source(ie.suitable)
  60. if hasattr(ie, '_make_valid_url'):
  61. # search extractors
  62. s += make_valid_template.format(valid_url=ie._make_valid_url())
  63. return s
  64. # find the correct sorting and add the required base classes so that subclasses
  65. # can be correctly created
  66. classes = _ALL_CLASSES[:-1]
  67. ordered_cls = []
  68. while classes:
  69. for c in classes[:]:
  70. bases = set(c.__bases__) - set((object, InfoExtractor, SearchInfoExtractor))
  71. stop = False
  72. for b in bases:
  73. if b not in classes and b not in ordered_cls:
  74. if b.__name__ == 'GenericIE':
  75. exit()
  76. classes.insert(0, b)
  77. stop = True
  78. if stop:
  79. break
  80. if all(b in ordered_cls for b in bases):
  81. ordered_cls.append(c)
  82. classes.remove(c)
  83. break
  84. ordered_cls.append(_ALL_CLASSES[-1])
  85. names = []
  86. for ie in ordered_cls:
  87. name = ie.__name__
  88. src = build_lazy_ie(ie, name)
  89. module_contents.append(src)
  90. if ie in _ALL_CLASSES:
  91. names.append(name)
  92. module_contents.append(
  93. '_ALL_CLASSES = [{0}]'.format(', '.join(names)))
  94. module_src = '\n'.join(module_contents) + '\n'
  95. with io.open(lazy_extractors_filename, 'wt', encoding='utf-8') as f:
  96. f.write(module_src)
  97. # work around JVM byte code module limit in Jython
  98. if sys.platform.startswith('java') and sys.version_info[:2] == (2, 7):
  99. import subprocess
  100. from youtube_dl.compat import compat_subprocess_get_DEVNULL
  101. # if Python 2.7 is available, use it to compile the module for Jython
  102. try:
  103. # if Python 2.7 is available, use it to compile the module for Jython
  104. subprocess.check_call(['python2.7', '-m', 'py_compile', lazy_extractors_filename], stdout=compat_subprocess_get_DEVNULL())
  105. except Exception:
  106. pass